package com.hobart.learning.lagou.edu.listener;

import com.hobart.learning.lagou.edu.DispatchServlet;
import com.hobart.learning.lagou.edu.annotation.*;
import com.hobart.learning.lagou.edu.factory.ProxyFactory;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author: Hobart Mr.Bai
 * @create: 2021-03-15 11:07
 * @description:
 * @Version 1.0
 **/
@WebListener
public class HobartServletContextListener implements ServletContextListener {

    // 第一步，通过反射技术实例化对象，并且存储进Map 集合
    private static Map<String, Object> map = new HashMap<>();
    private String packagePath = null;

    @Override
    public void contextInitialized(ServletContextEvent sce) {

        Class<DispatchServlet> dispatchServletClass = DispatchServlet.class;
        Package servletClassPackage = dispatchServletClass.getPackage();

        // 读取 项目 路径
        packagePath= System.getProperty("user.dir") + "\\src\\main\\java\\";
        String filePath = packagePath + servletClassPackage.getName().replace('.', '\\');
        try {
            // 加载 所有类 文件
            loadClass(new File(filePath));

            // 加载 所有类 文件 中属性
            loadClassField(new File(filePath));

            // 加载 所有类 文件 中 方法
            loadClassMethod(new File(filePath));
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println(" init ");
        map.forEach( (key, value) -> {
            System.out.println("key = " + key + ", value =" + value);
        });

        // 把它 存入 ServletContext 中 以便 全局 可以使用
        ServletContext servletContext = sce.getServletContext();
        map.forEach( (key, value) -> {
            servletContext.setAttribute(key, value);
        });
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("================================contextInitialized--------------------------");
    }

    private void loadClass(File file) throws ClassNotFoundException, InstantiationException, IllegalAccessException {

        if(file!=null&&file.exists()){
            if (file.isFile() && file.getName().lastIndexOf(".java") == file.getName().length() - 5) {

                String fileName = file.getName().split("\\.")[0];

                if(StringUtils.isNotEmpty(fileName)){

                    //5是".java"的长度
                    String filePath = file.getAbsolutePath();

                    String className = filePath.substring(packagePath.length(), filePath.length() - 5).replace('\\', '.');

                    // 通过反射 实例化 对象
                    Class<?> aClass = Class.forName(className);

                    // 加载 所有类
                    fileScanner(aClass, className);

                }

            } else if(file.isDirectory()) {
                for (File f : file.listFiles()){
                    loadClass(f);
                }
            }
        }
    }

    private void loadClassField(File file) throws ClassNotFoundException, InstantiationException, IllegalAccessException {

        if(file!=null&&file.exists()){
            if (file.isFile() && file.getName().lastIndexOf(".java") == file.getName().length() - 5) {

                String fileName = file.getName().split("\\.")[0];

                if(StringUtils.isNotEmpty(fileName)){

                    //5是".java"的长度
                    String filePath = file.getAbsolutePath();

                    String className = filePath.substring(packagePath.length(), filePath.length() - 5).replace('\\', '.');

                    // 通过反射 实例化 对象
                    Class<?> aClass = Class.forName(className);

                    // 加载  所有属性
                    constructorField(aClass, className);

                }

            } else if(file.isDirectory()) {
                // 是文件夹 递归调用
                for (File f : file.listFiles()){
                    loadClassField(f);
                }
            }
        }
    }

    private void loadClassMethod(File file) throws ClassNotFoundException, InstantiationException, IllegalAccessException {

        if(file!=null&&file.exists()){
            if (file.isFile() && file.getName().lastIndexOf(".java") == file.getName().length() - 5) {

                String fileName = file.getName().split("\\.")[0];

                if(StringUtils.isNotEmpty(fileName)){

                    //5是".java"的长度
                    String filePath = file.getAbsolutePath();

                    String className = filePath.substring(packagePath.length(), filePath.length() - 5).replace('\\', '.');

                    // 通过反射 实例化 对象
                    Class<?> aClass = Class.forName(className);

                    // 加载 方法
                    constructorMethod(aClass);
                }

            } else if(file.isDirectory()) {
                for (File f : file.listFiles()){
                    loadClassMethod(f);
                }
            }
        }
    }

    private void fileScanner(Class<?> aClass, String className) throws IllegalAccessException, InstantiationException {
        String lowerFileName = "";

        // 获取 所有 的类注解
        Annotation[] classAnnotations = aClass.getAnnotations();

        for (Annotation annotation : classAnnotations) {

            // 首字母 小写
            lowerFileName = toLowerCaseFirstOne(className);

            if(annotation instanceof Component && !aClass.isAnnotation()){

                // 实例化的对象
                Object beanObject =  aClass.newInstance();

                // 存储 byName
                map.put(lowerFileName, beanObject);

                break;
            }

            if(annotation instanceof Repository || annotation instanceof Service){

                Class<?>[] interfaces = aClass.getInterfaces();

                for (Class<?> anInterface : interfaces) {
                    // 实例化的对象
                    Object beanObject =  aClass.newInstance();

                    String interfaceName = anInterface.getName();

                    // 将其 接口 和 类 都存入
                    map.put(interfaceName, beanObject);
                    map.put(lowerFileName, beanObject);

                }

                break;
            }

        }
    }

    private void constructorField(Class<?> aClass, String className) throws IllegalAccessException, InstantiationException {

        // 实例化的对象
        // get 出 map 中的   className 对象 判断它是否已经存在
        // 如果 存在 则取出， 不存在 new
        Object beanObject = map.get(className);

        // 获取 所有 的类注解
        Annotation[] classAnnotations = aClass.getAnnotations();

        for (Annotation annotation : classAnnotations) {

            if(annotation instanceof Component && !aClass.isAnnotation()){

                if(beanObject==null){
                    beanObject = aClass.newInstance();
                }

                // 处理 所有属性
                loadField(aClass, beanObject);

                break;
            }

            if(annotation instanceof Repository || annotation instanceof Service){

                if(beanObject==null){
                    beanObject = aClass.newInstance();
                }

                // 处理 所有属性
                loadField(aClass, beanObject);

                break;
            }

        }
    }

    private void constructorMethod(Class<?> aClass) {

        Annotation[] classAnnotations = aClass.getAnnotations();

        for (Annotation annotation : classAnnotations) {

            if(annotation instanceof Component && !aClass.isAnnotation()){

                // 处理 所有 方法
                loadMethod(aClass);

                break;
            }

            if(annotation instanceof Repository || annotation instanceof Service){

                // 处理 所有 方法
                loadMethod(aClass);

                break;
            }

        }
    }

    private void loadField(Class<?> aClass, Object beanObject) throws IllegalAccessException, InstantiationException {
        Field[] declaredFields = aClass.getDeclaredFields();

        for (Field field : declaredFields) {

            Annotation[] fieldAnnotations = field.getAnnotations();

            for (Annotation fieldAnnotation : fieldAnnotations) {
                if(beanObject==null){
                    System.out.println("aClass = " + aClass + ", beanObject = " + beanObject);
                    beanObject =  aClass.newInstance();
                }
                field.setAccessible(true);
                if(fieldAnnotation instanceof Autowired){
                    Object o = map.get(field.getType().getName());
                    if(o!=null){
                        field.set(beanObject, o);

                        loadField(o.getClass(), o);
                    }
                }

            }
        }
    }

    private void loadMethod(Class<?> aClass) {
        Method[] methods = aClass.getMethods();

        for (Method method : methods) {
            Annotation[] methodAnnotations = method.getAnnotations();
            for (Annotation methodAnnotation : methodAnnotations) {

                if(methodAnnotation instanceof Transactional){

                    Class<?>[] interfaces = aClass.getInterfaces();

                    for (Class<?> anInterface : interfaces) {
                        String interfaceName = anInterface.getName();

                        ProxyFactory proxyFactory = (ProxyFactory) map.get(ProxyFactory.class.getName());
                        Object target = map.get(interfaceName);

                        System.out.println("target = " + proxyFactory.getJDKProxy(target));

                        map.put(interfaceName, proxyFactory.getJDKProxy(target));
                    }

                }

            }

        }

    }

    private String toLowerCaseFirstOne(String s){
        if(Character.isLowerCase(s.charAt(0))) {
            return s;
        } else {
            return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
        }
    }

    // 第二步，对外提供获取实例对象的接口
    public static Object getBean(String id) {
        return map.get(id);
    }
}
